/*-------------------------------------------------------------------------
 *
 * tstoreReceiver.c
 *	  DestReceiver的实现，存储结果元组在Tuplestore中。
 *
 * 可选地，我们可以强制对离线吐司值进行去吐司（但不进行解压缩）。这是为了支持必须在底层表被删除的情况下保留数据的WITH HOLD游标。
 *
 * 还可选地，我们可以在存储之前应用元组转换映射。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 * IDENTIFICATION
 *	  src/backend/executor/tstoreReceiver.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "access/detoast.h"
#include "access/tupconvert.h"
#include "executor/tstoreReceiver.h"


typedef struct
{
	DestReceiver pub;
	/* 参数： */
	Tuplestorestate *tstore;	/* 数据放置的位置 */
	MemoryContext cxt;			/* 包含 tstore 的上下文 */
	bool		detoast;		/* 我们是否被告知要解除压缩？ */
	TupleDesc	target_tupdesc; /* 目标 tupdesc，或如果没有则为 NULL */
	const char *map_failure_msg;	/* tupdesc 映射失败消息 */
	/* 工作区： */
	Datum	   *outvalues;		/* 结果元组的值数组 */
	Datum	   *tofree;			/* 要释放的临时值 */
	TupleConversionMap *tupmap; /* 如果需要的转换映射 */
	TupleTableSlot *mapslot;	/* 映射元组的槽 */
} TStoreState;


static bool fc_tstoreReceiveSlot_notoast(TupleTableSlot *fc_slot, DestReceiver *fc_self);
static bool fc_tstoreReceiveSlot_detoast(TupleTableSlot *fc_slot, DestReceiver *fc_self);
static bool fc_tstoreReceiveSlot_tupmap(TupleTableSlot *fc_slot, DestReceiver *fc_self);


/*
 * 准备接收来自执行器的元组。
 */
static void fc_tstoreStartupReceiver(DestReceiver *fc_self, int fc_operation, TupleDesc fc_typeinfo)
{
	TStoreState *fc_myState = (TStoreState *) fc_self;
	bool		fc_needtoast = false;
	int			fc_natts = fc_typeinfo->natts;
	int			fc_i;

	/* 检查是否有任何列需要解除压缩工作 */
	if (fc_myState->detoast)
	{
		for (fc_i = 0; fc_i < fc_natts; fc_i++)
		{
			Form_pg_attribute fc_attr = TupleDescAttr(fc_typeinfo, fc_i);

			if (fc_attr->attisdropped)
				continue;
			if (fc_attr->attlen == -1)
			{
				fc_needtoast = true;
				break;
			}
		}
	}

	/* 检查是否需要元组转换 */
	if (fc_myState->target_tupdesc)
		fc_myState->tupmap = convert_tuples_by_position(fc_typeinfo,
													 fc_myState->target_tupdesc,
													 fc_myState->map_failure_msg);
	else
		fc_myState->tupmap = NULL;

	/* 设置适当的回调 */
	if (fc_needtoast)
	{
		Assert(!fc_myState->tupmap);
		fc_myState->pub.receiveSlot = fc_tstoreReceiveSlot_detoast;
		/* 创建工作区 */
		fc_myState->outvalues = (Datum *)
			MemoryContextAlloc(fc_myState->cxt, fc_natts * sizeof(Datum));
		fc_myState->tofree = (Datum *)
			MemoryContextAlloc(fc_myState->cxt, fc_natts * sizeof(Datum));
		fc_myState->mapslot = NULL;
	}
	else if (fc_myState->tupmap)
	{
		fc_myState->pub.receiveSlot = fc_tstoreReceiveSlot_tupmap;
		fc_myState->outvalues = NULL;
		fc_myState->tofree = NULL;
		fc_myState->mapslot = MakeSingleTupleTableSlot(fc_myState->target_tupdesc,
													&TTSOpsVirtual);
	}
	else
	{
		fc_myState->pub.receiveSlot = fc_tstoreReceiveSlot_notoast;
		fc_myState->outvalues = NULL;
		fc_myState->tofree = NULL;
		fc_myState->mapslot = NULL;
	}
}

/*
 * 从执行器接收一个元组并将其存储在元组存储中。
 * 这是一个简单的情况，我们不需要解除压缩或映射任何内容。
 */
static bool fc_tstoreReceiveSlot_notoast(TupleTableSlot *fc_slot, DestReceiver *fc_self)
{
	TStoreState *fc_myState = (TStoreState *) fc_self;

	tuplestore_puttupleslot(fc_myState->tstore, fc_slot);

	return true;
}

/*
 * 从执行器接收一个元组并将其存储在元组存储中。
 * 这是一个需要解除压缩任何压缩值的情况。
 */
static bool fc_tstoreReceiveSlot_detoast(TupleTableSlot *fc_slot, DestReceiver *fc_self)
{
	TStoreState *fc_myState = (TStoreState *) fc_self;
	TupleDesc	fc_typeinfo = fc_slot->tts_tupleDescriptor;
	int			fc_natts = fc_typeinfo->natts;
	int			fc_nfree;
	int			fc_i;
	MemoryContext fc_oldcxt;

	/* 确保元组已完全解构 */
	slot_getallattrs(fc_slot);

	/*
	 * 取回任何不在行内的数据。 我们在
	 * myState->outvalues[] 中构建新的数据数组（但我们可以重用槽的 isnull 数组）。 此外，
	 * 记住取回的值以便之后释放。
	 */
	fc_nfree = 0;
	for (fc_i = 0; fc_i < fc_natts; fc_i++)
	{
		Datum		fc_val = fc_slot->tts_values[fc_i];
		Form_pg_attribute fc_attr = TupleDescAttr(fc_typeinfo, fc_i);

		if (!fc_attr->attisdropped && fc_attr->attlen == -1 && !fc_slot->tts_isnull[fc_i])
		{
			if (VARATT_IS_EXTERNAL(DatumGetPointer(fc_val)))
			{
				fc_val = PointerGetDatum(detoast_external_attr((struct varlena *)
															DatumGetPointer(fc_val)));
				fc_myState->tofree[fc_nfree++] = fc_val;
			}
		}

		fc_myState->outvalues[fc_i] = fc_val;
	}

	/*
	 * 将修改后的元组推入元组存储。
	 */
	fc_oldcxt = MemoryContextSwitchTo(fc_myState->cxt);
	tuplestore_putvalues(fc_myState->tstore, fc_typeinfo,
						 fc_myState->outvalues, fc_slot->tts_isnull);
	MemoryContextSwitchTo(fc_oldcxt);

	/* 并释放任何临时解除压缩的值 */
	for (fc_i = 0; fc_i < fc_nfree; fc_i++)
		pfree(DatumGetPointer(fc_myState->tofree[fc_i]));

	return true;
}

/*
 * 从执行器接收一个元组并将其存储在元组存储中。
 * 这是在我们必须应用元组转换映射的情况下。
 */
static bool fc_tstoreReceiveSlot_tupmap(TupleTableSlot *fc_slot, DestReceiver *fc_self)
{
	TStoreState *fc_myState = (TStoreState *) fc_self;

	execute_attr_map_slot(fc_myState->tupmap->attrMap, fc_slot, fc_myState->mapslot);
	tuplestore_puttupleslot(fc_myState->tstore, fc_myState->mapslot);

	return true;
}

/*
 * 在执行器运行结束时进行清理
 */
static void fc_tstoreShutdownReceiver(DestReceiver *fc_self)
{
	TStoreState *fc_myState = (TStoreState *) fc_self;

	/* 如果有工作区则释放 */
	if (fc_myState->outvalues)
		pfree(fc_myState->outvalues);
	fc_myState->outvalues = NULL;
	if (fc_myState->tofree)
		pfree(fc_myState->tofree);
	fc_myState->tofree = NULL;
	if (fc_myState->tupmap)
		free_conversion_map(fc_myState->tupmap);
	fc_myState->tupmap = NULL;
	if (fc_myState->mapslot)
		ExecDropSingleTupleTableSlot(fc_myState->mapslot);
	fc_myState->mapslot = NULL;
}

/*
 * 完成后销毁接收器
 */
static void fc_tstoreDestroyReceiver(DestReceiver *fc_self)
{
	pfree(fc_self);
}

/*
 * 最初创建一个 DestReceiver 对象。
 */
DestReceiver * CreateTuplestoreDestReceiver(void)
{
	TStoreState *fc_self = (TStoreState *) palloc0(sizeof(TStoreState));

	fc_self->pub.receiveSlot = fc_tstoreReceiveSlot_notoast;	/* 可能会改变 */
	fc_self->pub.rStartup = fc_tstoreStartupReceiver;
	fc_self->pub.rShutdown = fc_tstoreShutdownReceiver;
	fc_self->pub.rDestroy = fc_tstoreDestroyReceiver;
	fc_self->pub.mydest = DestTuplestore;

	/* 私有字段将通过 SetTuplestoreDestReceiverParams 设置 */

	return (DestReceiver *) fc_self;
}

/*
 * 为 TuplestoreDestReceiver 设置参数
 *
 * tStore: 存储元组的地方
 * tContext: 包含 tStore 的内存上下文
 * detoast: 是否强制进行去烤操作以解压缩包含的数据？
 * target_tupdesc: 如果不为 NULL，强制将元组转换为此行类型
 * map_failure_msg: 如果映射到 target_tupdesc 失败时使用的错误消息
 *
 * 我们目前不支持同时使用 detoast 和 target_tupdesc，
 * 只是因为没有现有的调用者需要这种组合。
 */
void SetTuplestoreDestReceiverParams(DestReceiver *fc_self,
								Tuplestorestate *fc_tStore,
								MemoryContext fc_tContext,
								bool fc_detoast,
								TupleDesc fc_target_tupdesc,
								const char *fc_map_failure_msg)
{
	TStoreState *fc_myState = (TStoreState *) fc_self;

	Assert(!(fc_detoast && fc_target_tupdesc));

	Assert(fc_myState->pub.mydest == DestTuplestore);
	fc_myState->tstore = fc_tStore;
	fc_myState->cxt = fc_tContext;
	fc_myState->detoast = fc_detoast;
	fc_myState->target_tupdesc = fc_target_tupdesc;
	fc_myState->map_failure_msg = fc_map_failure_msg;
}
